//
//  Extension.swift
//  SearchHeader
//
//  Created by 万孟超 on 2023/10/11.
//

import Cocoa

enum QueueType {
    case main
    case global(qos: DispatchQoS.QoSClass = .default, after: TimeInterval? = nil)
    case custom(name: String)
}

func Async(_ type: QueueType, block: @escaping @convention(block) () -> ()) {
    switch type {
        case .main:
            DispatchQueue.main.async {
                block()
            }
        case .global(let qos, let after):
            guard let after = after else {
                DispatchQueue.global(qos: qos).async {
                    block()
                }
                return
            }
            DispatchQueue.global(qos: qos).asyncAfter(wallDeadline: .now() + after ) {
                block()
            }
        case .custom(let name):
            DispatchQueue(label: name, attributes: [.concurrent]).async {
                block()
            }
    }
}

private let mainLock = NSLock()
private let globalLock = NSLock()
private let globalAfterLock = NSLock()
private let customLock = NSLock()

func safeAsync(_ type: QueueType, block: @escaping @convention(block) () -> ()) {
    switch type {
        case .main:
            DispatchQueue.main.async {
                mainLock.lock()
                block()
                mainLock.unlock()
            }
        case .global(let qos, let after):
            guard let after = after else {
                DispatchQueue.global(qos: qos).async {
                    globalLock.lock()
                    block()
                    globalLock.unlock()
                }
                return
            }
            DispatchQueue.global(qos: qos).asyncAfter(wallDeadline: .now() + after ) {
                globalAfterLock.lock()
                block()
                globalAfterLock.unlock()
            }
        case .custom(let name):
            DispatchQueue(label: name, attributes: [.concurrent]).async {
                customLock.lock()
                block()
                customLock.unlock()
            }
    }
}

let replaceFileNotification = Notification.Name("ReplaceFile")

extension Dictionary {
    var jsonValue : String {
        var jsonData : Data? = nil
        do {
            jsonData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
        } catch let error {
            print(error.localizedDescription)
        }
        if let jsonData = jsonData {
            return String(data: jsonData, encoding: .utf8) ?? ""
        }else{
            return ""
        }
    }
    
}

extension Array where Element: Equatable {
    mutating func appendElement(_ element: Element) {
        if !self.contains(where: {$0 == element}) {
            append(element)
        }
    }
    
    mutating func removeObject(_ element: Element) {
        self.removeAll(where: {$0 == element})
    }
}

extension NSColor {
    static func randomColor(alpha: CGFloat = 1) -> NSColor {
        return NSColor(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1), alpha: alpha)
    }
}

fileprivate var viewTagKey: UInt8 = 0
fileprivate var animationLayerKey: UInt8 = 0

extension NSView {
    
    var viewTag: Int {
        set {
            objc_setAssociatedObject(self, &viewTagKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
        get {
            return objc_getAssociatedObject(self, &viewTagKey) as? Int ?? -1
        }
    }
    
    var animationLayer: CAGradientLayer? {
        set {
            objc_setAssociatedObject(self, &animationLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            return objc_getAssociatedObject(self, &animationLayerKey) as? CAGradientLayer
        }
    }
    
    enum GradientLocationType {
        case TopToBottom
        case LeftToRight
        case UpLeftToBottomRight
        case UpRightToBottomLeft
    }
    
    @discardableResult
    /// 添加渐变色背景
    /// - Parameters:
    ///   - colors: 渐变色数组
    ///   - gradientType: 渐变色位置
    ///   - locations: 渐变色Location
    ///   - radius: 圆角
    ///   - at: 添加的index
    /// - Returns: 渐变色画布
    func addGradientBackgroundColor(colors: [NSColor], gradientType : GradientLocationType, locations: [NSNumber] = [0, 1], radius: CGFloat = 0, at: UInt32 = 0, boundsSize : NSSize? = nil) -> CAGradientLayer? {
        let bgLayer = CAGradientLayer()
        bgLayer.colors = colors.map({ $0.cgColor })
        bgLayer.locations = locations
        if let boundsSize = boundsSize {
            bgLayer.frame = CGRect(origin: .zero, size: boundsSize)
        }else{
            bgLayer.frame = bounds
        }
        var start : CGPoint = CGPoint(x: 0, y: 0)
        var end : CGPoint = CGPoint(x: 0, y: 0)
        switch gradientType {
            case .TopToBottom:
                start = CGPoint.init(x: 0, y: 0)
                end = CGPoint.init(x: 0, y: 1)
            case .LeftToRight:
                start = CGPoint.init(x: 0, y: 0)
                end = CGPoint.init(x: 1, y: 0)
            case .UpLeftToBottomRight:
                start = CGPoint.init(x: 0, y: 0)
                end = CGPoint.init(x: 1, y: 1)
            case .UpRightToBottomLeft:
                start = CGPoint.init(x: 1, y: 0)
                end = CGPoint.init(x: 0, y: 1)
        }
        bgLayer.startPoint = start
        bgLayer.endPoint = end
        bgLayer.cornerRadius = radius
        layer?.insertSublayer(bgLayer, at: at)
        return bgLayer
    }
    
    func addAnimatedGradientBorder() {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        gradientLayer.colors = [NSColor.randomColor().cgColor, NSColor.randomColor().cgColor, NSColor.randomColor().cgColor, NSColor.randomColor().cgColor, NSColor.randomColor().cgColor,NSColor.randomColor().cgColor,NSColor.randomColor().cgColor,NSColor.randomColor().cgColor]
        gradientLayer.startPoint = CGPoint(x: 0, y: 1)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = 4.0
        let bezierPath = NSBezierPath()
        bezierPath.move(to: CGPoint(x: bounds.width / 2, y: 0))
        bezierPath.appendArc(withCenter: CGPoint(x: bounds.width - (layer?.cornerRadius ?? 0), y: bounds.height / 2),
                             radius: (layer?.cornerRadius ?? 0),
                             startAngle: -90,
                             endAngle: 90,
                             clockwise: false)
        bezierPath.line(to: CGPoint(x: bounds.width - (layer?.cornerRadius ?? 0), y: bounds.height))
        bezierPath.appendArc(withCenter: CGPoint(x: (layer?.cornerRadius ?? 0), y: bounds.height / 2), 
                             radius: (layer?.cornerRadius ?? 0),
                             startAngle: 90,
                             endAngle: 270,
                             clockwise: false)
        bezierPath.line(to: CGPoint(x: bounds.width / 2, y: 0))
        shapeLayer.path = convertBezierPathToCGPath(bezierPath:bezierPath)
        shapeLayer.strokeColor = NSColor.red.cgColor
        shapeLayer.fillColor = NSColor.clear.cgColor
        shapeLayer.strokeStart = 0
        shapeLayer.strokeEnd = 1
        gradientLayer.mask = shapeLayer

        layer?.addSublayer(gradientLayer)
        
        let animationGroup = CAAnimationGroup()
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = 2
        let animation1 = CABasicAnimation(keyPath: "strokeStart")
        animation1.fromValue = 0
        animation1.toValue = 1
        animation1.duration = 2
        animation1.beginTime = animation.duration
        animationGroup.animations = [animation1, animation]
        animationGroup.duration = 4
        animationGroup.repeatCount = .infinity
        
        // 将动画添加到图层上
        shapeLayer.add(animationGroup, forKey: nil)
        animationLayer = gradientLayer
    }
    
    func stopAnimation() {
        Async(.main) {[self] in
            animationLayer?.removeFromSuperlayer()
        }
    }
    
   private func convertBezierPathToCGPath(bezierPath: NSBezierPath) -> CGPath {
        let path = CGMutablePath()
        let numElements = bezierPath.elementCount
        
        if numElements > 0 {
            var didClosePath = true
            var points = [NSPoint](repeating: NSZeroPoint, count: 3)
            
            for i in 0..<numElements {
                let pathType = bezierPath.element(at: i, associatedPoints: &points)
                
                switch pathType {
                    case .moveTo:
                        path.move(to: points[0])
                    case .lineTo:
                        path.addLine(to: points[0])
                        didClosePath = false
                    case .curveTo:
                        path.addCurve(to: points[2], control1: points[0], control2: points[1])
                        didClosePath = false
                    case .closePath:
                        path.closeSubpath()
                        didClosePath = true
                    default:
                        path.addCurve(to: points[2], control1: points[0], control2: points[1])
                        didClosePath = false
                        break
                }
            }
            
            if !didClosePath {
                path.closeSubpath()
            }
        }
        
        return path
    }
}

extension String {
    func contains(_ collection: [Self]) -> Bool {
        for str in collection {
            if contains(str) {
                return true
            }
        }
        return false
    }
    
    func isEqualTo(_ collection: [Self]) -> Bool {
        for str in collection {
            if pathExtension == str {
                return true
            }
        }
        return false
    }
    
    func replace(_ collection: [Self] , with string: any StringProtocol) -> Self {
        for str in collection {
            if contains(str) {
                return replacingOccurrences(of: str, with: string)
            }
        }
        return self
    }
    
    var simplePath: Self {
        var temppath = replacingOccurrences(of: "//", with: "/")
        temppath = temppath.replacingOccurrences(of: "/./", with: "/")
        let pattern = "/[a-zA-Z]+/\\.{2}/"
        while temppath.contains("/../") {
            do {
                while temppath.starts(with: "/../") {
                    temppath = String(temppath[temppath.index(temppath.startIndex, offsetBy: 3)..<temppath.endIndex])
                }
                let regex = try NSRegularExpression(pattern: pattern)
                temppath = regex.stringByReplacingMatches(in: temppath, range: NSRange(location: 0, length: temppath.count), withTemplate: "/")
            } catch _ {
                return ""
            }
        }
        
        if temppath[temppath.index(temppath.endIndex, offsetBy: -1)] == "/" && temppath.count != 1 {
            temppath.removeLast()
        }
        return temppath
    }
    
    var attributedString: NSAttributedString {
        let attributedString = NSMutableAttributedString(string: self)
        let font = NSFont.systemFont(ofSize: 14)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = 10 // 设置行间距为10
        attributedString.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, attributedString.length))
        attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length))
        return attributedString
    }
    
    var filterOutSpecialChar: Self {
        do {
            let pattern = "/\\*[\\s\\S]*?\\*/|\\#|\\/\\/.*|\n|\\:|\\;|\\[|\\]|\\.|\\@|\\(|\\)|\\<|\\>|\\/|\\,|\\{|\\}|\\.|\\!|\\?|\\=|\"|\\*"
            let regex = try NSRegularExpression(pattern: pattern, options: .init(rawValue: 0))
            return regex.stringByReplacingMatches(in: self, options: .reportProgress, range: NSMakeRange(0, count), withTemplate: " ")
        }catch {
            
        }
        return self
    }
    
    var pathExtension: Self {
        let arr = components(separatedBy: ".")
        return arr.last ?? ""
    }
    
    var jsonToDic : [String : Any]? {
        let jsonData : Data = data(using: .utf8)!
        let dic = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
        if dic != nil {
            return dic as? [String : Any]
        }
        return nil
    }
    
    var jsonToArray : Array<Any>? {
        let jsonData : Data = data(using: .utf8)!
        let array = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
        if array != nil {
            return array as? [Any]
        }
        return nil
    }
    
    var upperFirstLetter: Self {
        return Self(prefix(1).capitalized + dropFirst())
    }
    
    /// 十六进制字符串颜色转为UIColor
    /// - Parameter alpha: 透明度
    func color(alpha: CGFloat = 1.0) -> NSColor {
        // 存储转换后的数值
        var red: UInt64 = 0, green: UInt64 = 0, blue: UInt64 = 0
        var hex = self
        // 如果传入的十六进制颜色有前缀，去掉前缀
        if hex.hasPrefix("0x") || hex.hasPrefix("0X") {
            hex = String(hex[hex.index(hex.startIndex, offsetBy: 2)...])
        } else if hex.hasPrefix("#") {
            hex = String(hex[hex.index(hex.startIndex, offsetBy: 1)...])
        }
        // 如果传入的字符数量不足6位按照后边都为0处理，当然你也可以进行其它操作
        if hex.count < 6 {
            for _ in 0..<6-hex.count {
                hex += "0"
            }
        }
        // 分别进行转换
        // 红
        Scanner(string: String(hex[..<hex.index(hex.startIndex, offsetBy: 2)])).scanHexInt64(&red)
        // 绿
        Scanner(string: String(hex[hex.index(hex.startIndex, offsetBy: 2)..<hex.index(hex.startIndex, offsetBy: 4)])).scanHexInt64(&green)
        // 蓝
        Scanner(string: String(hex[hex.index(startIndex, offsetBy: 4)...])).scanHexInt64(&blue)
        return NSColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: alpha)
    }
    
    func ranges(of searchString: String) -> [NSRange] {
        var ranges = [NSRange]()
        let string = self as NSString
        var searchRange = NSRange(location: 0, length: string.length)
        
        while searchRange.location != NSNotFound {
            searchRange = string.range(of: searchString, options: [], range: searchRange)
            if searchRange.location != NSNotFound {
                ranges.append(searchRange)
                searchRange.location = searchRange.location + searchRange.length
                searchRange.length = string.length - searchRange.location
            }
        }
        return ranges
    }
}

extension NSMutableAttributedString {
    func addAttribute(_ key: NSAttributedString.Key, value: Any, ranges: [NSRange]) {
        for range in ranges {
            addAttribute(key, value: value, range: range)
        }
    }
}

class VerticalCell: NSTextFieldCell {
    
    override func drawingRect(forBounds theRect: NSRect) -> NSRect {
        var newRect: NSRect = super.drawingRect(forBounds: theRect)
        let textSize: NSSize = cellSize(forBounds: theRect)
        let heightDelta: CGFloat = newRect.size.height - textSize.height
        if heightDelta > 0 {
            newRect.size.height = textSize.height
            newRect.origin.y += heightDelta * 0.5
        }
        return newRect
    }
}


extension NSTextField {
    
    var isVerticalCenter: Bool {
        set { verticalText() }
        get { false }
    }
    
    func verticalText() {
        cell = VerticalCell()
        
    }

}

extension NSScrollView {
    
    func scrollToTop() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0) {[self] in
            if let documentView = self.documentView {
                if documentView.isFlipped {
                    documentView.scroll(.zero)
                } else {
                    let maxHeight = max(bounds.height, documentView.bounds.height)
                    documentView.scroll(NSPoint(x: 0, y: maxHeight))
                }
            }
        }
    }
    
}


extension NSPopover {
    static func showPopover(_ alertArray: [String], of view: NSView, position: NSRectEdge = .maxX) {
        let popover = Self()
        let contentController = NSViewController()
        let contentView = PopoverContent(dataArray: alertArray)
        contentController.view = contentView
        popover.contentViewController = contentController
        popover.behavior = .transient
        popover.show(relativeTo: view.bounds, of: view, preferredEdge: position)
    }
}

extension NSTextField {
    open override func performKeyEquivalent(with event: NSEvent) -> Bool {
        switch event.charactersIgnoringModifiers {
            case "a":
                return NSApp.sendAction(#selector(NSText.selectAll(_:)), to: self.window?.firstResponder, from: self)
            case "c":
                return NSApp.sendAction(#selector(NSText.copy(_:)), to: self.window?.firstResponder, from: self)
            case "v":
                return NSApp.sendAction(#selector(NSText.paste(_:)), to: self.window?.firstResponder, from: self)
            case "x":
                return NSApp.sendAction(#selector(NSText.cut(_:)), to: self.window?.firstResponder, from: self)
            default:
                return super.performKeyEquivalent(with: event)
        }
    }
}

extension Double {
    var size: String {
        if self < 1024 {
            return "0KB"
        }
        if self >= 1024 && self < 1024 * 1024 {
            return String(format: "%.1fKB", self / 1024.0)
        }
        if self >= 1024 * 1024 && self < 1024 * 1024 * 1024 {
            return String(format: "%.2fMB", self / 1024.0 / 1024.0)
        }
        if self >= 1024 * 1024 * 1024 {
            return String(format: "%.2fGB", self / 1024.0 / 1024.0 / 1024.0)
        }
        return "0KB"
    }
}

extension NSImage {
    func image(color: NSColor) -> NSImage {
        guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else {
            return self
        }
        
        let ciImage = CIImage(cgImage: cgImage)
        
        // 创建颜色滤镜
        guard let colorFilter = CIFilter(name: "CIFalseColor") else {
            return self
        }
        colorFilter.setValue(CIColor(cgColor: color.cgColor), forKey: "inputColor0")
        colorFilter.setValue(ciImage, forKey: kCIInputImageKey)
        
        // 应用滤镜
        guard let outputImage = colorFilter.outputImage else {
            return self
        }
        
        // 创建图形上下文
        let ciContext = CIContext(options: nil)
        guard let cgImageOutput = ciContext.createCGImage(outputImage, from: outputImage.extent) else {
            return self
        }
        
        // 创建新的 NSImage
        let newImage = NSImage(cgImage: cgImageOutput, size: size)
        
        return newImage
    }
    
}
